home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 090 / byteibm.arc / EMSDISK.ASM < prev    next >
Assembly Source File  |  1985-07-12  |  30KB  |  1,117 lines

  1. \foot4
  2. \ctr\- \%page\ -
  3. \right
  4. 605057
  5. \left 
  6. \lm0
  7. \rm80
  8. \hy
  9. \ssa
  10. \ssb
  11. \bf
  12. \un
  13.     name    emsdisk
  14.     page    55,132
  15.     title    EMSDISK for Expanded Memory 
  16.  
  17. ; EMSDISK for Lotus/Intel/Microsoft Expanded Memory
  18. ;
  19. ; Copyright (C) 1986 Ray Duncan
  20. ; version 1.0 May 1986
  21. ; This program may be freely reproduced and modified for
  22. ; non-commercial (personal) use. It may not be resold or
  23. ; incorporated into products for resale without written 
  24. ; permission from the author.
  25. ;
  26. ; To convert EMSDISK.ASM into the executable EMSDISK.BIN:
  27. ;    C>MASM EMSDISK;
  28. ;    C>LINK EMSDISK;
  29. ;    C>EXE2BIN EMSDISK.EXE EMSDISK.BIN
  30. ;    C>DEL EMSDISK.EXE
  31. ;
  32. ; To link the EMSDISK into the MS-DOS operating system,
  33. ; copy the EMSDISK.BIN file to your boot disk and add the line:
  34. ;
  35. ;    DEVICE=EMSDISK.BIN  nnnK
  36. ; into the CONFIG.SYS file on your boot disk (where nnnK is the
  37. ; desired EMSDISK size in Kbytes) AFTER the line that loads
  38. ; the Expanded Memory Manager (EMM.SYS for Intel Above Board).
  39. ;
  40.  
  41. code    segment public 'CODE'
  42.  
  43.     assume    cs:code,ds:code,es:code
  44.  
  45.     org    0
  46.  
  47. cc2_max equ    12        ; max driver command code for DOS 2
  48. cc3_max equ    16        ; max driver command code for DOS 3 
  49.  
  50. cr    equ    0dh        ; ASCII carriage return
  51. lf    equ    0ah        ; ASCII line feed
  52. blank    equ    020h        ; ASCII space code
  53. eom    equ    '$'        ; end of message signal
  54.  
  55. emm_int equ    67h        ; Software Interrupt for communication
  56.                 ; with Expanded Memory Manager
  57.  
  58. page_size equ    16384        ; bytes per logical EMS page
  59.  
  60. sec_size equ    512        ; bytes per logical sector, should
  61.                 ; be some multiple of 512 <= 16384. 
  62.  
  63. dir_size equ    256        ; entries in root directory        
  64.  
  65.                 ; logical sectors per EMS page
  66. sec_per_page equ page_size/sec_size 
  67.  
  68.     page
  69.  
  70. ; MS-DOS Request Header structure definition
  71.  
  72. request struc            ; request header template structure
  73.                 ; beginning of "Static" portion
  74. rlength db    ?        ; length of request header
  75. unit    db    ?        ; unit number for this request
  76. command db    ?        ; request header's command code
  77. status    dw    ?        ; driver's return status word
  78.                 ; bit  15    = Error
  79.                 ; bits 10-14 = Reserved
  80.                 ; bit  9     = Busy
  81.                 ; bit  8     = Done
  82.                 ; bits 0-7   = Error code if bit 15=1
  83. reserve db    8 dup (?)    ; reserved area
  84.                 ; end of "Static" portion, the remainder 
  85.                 ; is struc. for Read and Write commands
  86. media    db    ?        ; media descriptor byte
  87. address dd    ?        ; memory address for transfer
  88. count    dw    ?        ; byte/sector count value
  89. sector    dw    ?        ; starting sector value
  90. request ends            ; end of request header template
  91.  
  92.     page
  93.  
  94. ;
  95. ; Device Driver header
  96. ;
  97. header        dd    -1    ; link to next device driver in chain
  98.         dw    0    ; device attribute word
  99.                 ; bit 15 =1 for character devices
  100.                 ;     =0 for block devices
  101.                 ; bit 14 =1 if driver can handle IOCTL
  102.                 ; bit 13 =1 if block device & non-IBM format
  103.                 ; bit 12    reserved
  104.                 ; bit 11 =1 if OPEN/CLOSE/RM supported (DOS 3)
  105.                 ; bits 4-10 reserved
  106.                 ; bit 3  =1 if CLOCK device
  107.                 ; bit 2  =1 if NUL device
  108.                 ; bit 1  =1 if Standard Output
  109.                 ; bit 0  =1 if Standard Input
  110.         dw    strat    ; device "Strategy" entry point
  111.         dw    intr    ; device "Interrupt" entry point
  112.         db    1    ; number of units, this device
  113.         db    7 dup (0) ; reserved area (block dev. drivers)
  114.  
  115. rh_ptr        dd    ?    ; double word pointer to Request header
  116.  
  117. save_sp     dw    0    ; save DOS's SS:SP
  118. save_ss     dw    0
  119.  
  120. avail_pages    dw    0    ; logical EMS pages available
  121. total_pages    dw    0    ; total logical EMS pages in system
  122. req_pages    dw    0    ; EMSDISK requested size in EMS pages
  123. owned_pages    dw    0    ; logical EMS pages owned by EMSDISK
  124. page_frame    dw    0    ; segment address of page frame
  125. emm_handle    dw    0    ; EMSDISK EMM handle (process id) 
  126.  
  127. dos_ver     db    0    ; DOS major version no. 
  128. max_cmd     dw    0    ; maximum command code, this DOS version
  129.  
  130. xfer_sec    dw    0    ; current sector for transfer
  131. xfer_cnt    dw    0    ; sectors successfully transferred
  132. xfer_req    dw    0    ; number of sectors requested    
  133. xfer_addr    dd    0    ; working address for transfer 
  134.  
  135. bpb_array    dw    bpb    ; array of pointers to BPB for each unit
  136.  
  137.                 ; copy of CONFIG.SYS line for driver
  138. cmd_line    db    80 dup (0)
  139.  
  140.         even        ; force word alignment
  141.         dw    128 dup (0)
  142. stk        equ    $    ; local stack for use by driver
  143.  
  144.     page
  145.  
  146. boot_rec    equ    $
  147.  
  148.         jmp    $        ; phony JMP at start of
  149.         nop            ; boot sector, this field
  150.                     ; must be 3 bytes.
  151.  
  152.         db    'EMRAMDSK'    ; OEM identity field
  153.  
  154.                     ; BIOS Parameter Block (BPB).
  155.                     ; "sectors per cluster", "total
  156.                     ; sectors" & "sectors per FAT"
  157.                     ; are updated by "setup" procedure
  158. bpb        dw    sec_size    ; 0   bytes per sector
  159. sec_clus    db    0        ; 2   sectors per cluster
  160.         dw    1        ; 3   reserved sectors
  161.         db    1        ; 5   number of FATs
  162.         dw    dir_size    ; 6   root directory entries
  163. tot_sec     dw    0        ; 8   total sectors
  164.         db    0f8h        ; 0AH media descriptor
  165. sec_fat     dw    0        ; 0BH sectors per FAT
  166.  
  167. boot_rec_len    equ    $-boot_rec    ; length to copy to
  168.                     ; EMSDISK logical sector 0
  169.  
  170.  
  171.     page
  172.  
  173. ; EMSDISK Device Driver "Strategy Routine"
  174. ;
  175. ; Each time a request is made for the logical unit assigned
  176. ; to the EMSDISK, MS-DOS first calls the "Strategy Routine",  
  177. ; then immediately calls the "Interrupt Routine".  
  178. ;
  179. ; The Strategy Routine is passed the address of the
  180. ; Request header in ES:BX, which it saves in a local
  181. ; variable and then returns to MS-DOS.
  182.  
  183. strat    proc    far    
  184.                 ; save address of Request header
  185.     mov    word ptr cs:[rh_ptr],bx
  186.     mov    word ptr cs:[rh_ptr+2],es
  187.  
  188.     ret            ; back to MS-DOS
  189.  
  190. strat    endp
  191.  
  192.     page
  193.  
  194.  
  195. ; EMSDISK Device Driver "Interrupt Routine"
  196. ;
  197. ; This entry point is called by MS-DOS immediately after 
  198. ; the call to the "Strategy Routine", which saved the long
  199. ; address of the Request header in the local variable "rh_ptr".
  200.  
  201. ; The "Interrupt Routine" uses the Command Code passed in
  202. ; the Request header to transfer to the appropriate device
  203. ; handling routine.  Each Command Code subroutine returns
  204. ; with AX=status.
  205.  
  206. intr    proc    far
  207.  
  208.     push    ax        ; save general registers 
  209.     push    bx
  210.     push    cx
  211.     push    dx
  212.     push    ds
  213.     push    es
  214.     push    di
  215.     push    si
  216.     push    bp
  217.  
  218.     mov    ax,cs        ; make local data addressable
  219.     mov    ds,ax
  220.  
  221.     mov    save_ss,ss    ; save DOS's stack pointers
  222.     mov    save_sp,sp
  223.  
  224.     mov    ss,ax        ; set SS:SP to point to 
  225.     mov    sp,offset stk    ; (larger) local stack
  226.  
  227.     les    di,[rh_ptr]    ; let ES:DI = Request header
  228.  
  229.                 ; get BX = command Code
  230.     mov    bl,es:[di.command]
  231.     xor    bh,bh
  232.     cmp    bx,max_cmd    ; make sure it's legal
  233.     jle    intr1        ; jump, function code is ok
  234.     mov    ax,8003h    ; set Error bit and "Unknown command" code
  235.     jmp    intr3
  236.  
  237. intr1:    or    bx,bx        ; is it init call? (function 0)
  238.     jz    intr2        ; yes, skip save of context
  239.  
  240.     mov    ah,47h        ; save EMM page map for
  241.     mov    dx,emm_handle    ; the interrupted process
  242.     int    67h
  243.  
  244.     or    ah,ah        ; jump if EMM error while
  245.     jnz    intr9        ; saving page mapping context
  246.  
  247. intr2:    shl    bx,1        ; form index to dispatch table and
  248.                 ; branch to command code routine
  249.     call    word ptr [bx+dispatch]
  250.                 ; should return AX = status
  251.  
  252.     les    di,[rh_ptr]    ; restore ES:DI = addr of Request header
  253.  
  254. intr3:    or    ax,0100h    ; merge Done bit into status, and
  255.                 ; store into Request header
  256.     mov    es:[di.status],ax
  257.  
  258.                 ; Was this initialization call?
  259.     mov    bl,es:[di.command]
  260.     or    bl,bl
  261.     jz    intr4        ; yes, skip restore of context
  262.  
  263.     mov    ah,48h        ; restore EMM page map 
  264.     mov    dx,emm_handle    ; for interrupted process
  265.     int    67h
  266.  
  267.     or    ah,ah        ; jump if EMM error while 
  268.     jnz    intr9        ; restoring page mapping 
  269.  
  270. intr4:                ; central exit point from
  271.                 ; driver's "INTR" routine
  272.  
  273.     mov    ss,save_ss    ; restore DOS's stack
  274.     mov    sp,save_sp
  275.  
  276.     pop    bp        ; restore general registers
  277.     pop    si
  278.     pop    di
  279.     pop    es
  280.     pop    ds
  281.     pop    dx
  282.     pop    cx
  283.     pop    bx
  284.     pop    ax
  285.     ret            ; back to MS-DOS
  286.  
  287. intr9:                ; come here if disastrous EMM error 
  288.                 ; encountered to set "General Failure" 
  289.                 ; error code and return to MS-DOS
  290.     
  291.     les    di,[rh_ptr]    ; ES:DI = addr of Request header
  292.  
  293.                 ; Set Error bit, Done bit, and 
  294.                 ; Error Code 12 (0CH)
  295.     mov    es:[di.status],810ch
  296.     jmp    intr4
  297.  
  298. intr    endp
  299.  
  300.  
  301. ; MS-DOS Command Codes dispatch table.    The "Interrupt" routine uses 
  302. ; this table and the Command Code supplied in the Request Header to 
  303. ; transfer to the appropriate driver subroutine.  Table entries for 
  304. ; command codes not supported by this driver point to a dummy routine 
  305. ; that only sets the "done" status and exits.
  306.  
  307. dispatch:
  308.     dw    init        ;  0 = initialize driver
  309.     dw    media_chk    ;  1 = media check on block device
  310.     dw    build_bpb    ;  2 = build BIOS parameter block
  311.     dw    dummy        ;  3 = I/O control read 
  312.     dw    read        ;  4 = read from device
  313.     dw    dummy        ;  5 = non-destructive read 
  314.     dw    dummy        ;  6 = return current input status
  315.     dw    dummy        ;  7 = flush device input buffers
  316.     dw    write        ;  8 = write to device
  317.     dw    write        ;  9 = write with verify
  318.     dw    dummy        ; 10 = return current output status
  319.     dw    dummy        ; 11 = flush output buffers
  320.     dw    dummy        ; 12 = I/O control write 
  321.     dw    dummy        ; 13 = device open     (DOS 3.X)
  322.     dw    dummy        ; 14 = device close     (DOS 3.X)
  323.     dw    dummy        ; 15 = removeable media  (DOS 3.X)
  324.     dw    dummy        ; 16 = output until busy (DOS 3.X)   
  325.  
  326.     page
  327.  
  328. ;
  329. ; Command Code subroutines called by Interrupt Routine
  330. ;
  331. ; These routines are called with ES:DI = Request Header.  
  332. ;
  333. ; They should return AX = 0 if function was completed
  334. ; successfully, or AX = 8000H + Error code if function failed.
  335. ;
  336.  
  337. media_chk proc    near        ; command code 1 = Media Check
  338.  
  339.                 ; return "not changed" code
  340.     mov    byte ptr es:[di+14],1
  341.  
  342.     xor    ax,ax        ; set "done" status
  343.     ret
  344.  
  345. media_chk endp
  346.  
  347.  
  348. build_bpb proc    near        ; command code 2 = Build BPB
  349.  
  350.                 ; return BPB address in request header
  351.     mov    word ptr es:[di+18],offset bpb
  352.     mov    word ptr es:[di+20],cs    
  353.  
  354.     xor    ax,ax        ; set "done" status
  355.     ret
  356.  
  357. build_bpb endp
  358.  
  359.  
  360. read    proc    near        ; command code 4 = Read
  361.  
  362.     call    init_xfer    ; set up local variables 
  363.  
  364. read1:    mov    ax,xfer_cnt    ; done with all sectors yet?
  365.     cmp    ax,xfer_req
  366.     je    read2        ; jump if transfer completed
  367.     mov    ax,xfer_sec    ; get next sector number
  368.     call    map_sec     ; and map it
  369.     jc    read4        ; jump if mapping error
  370.     les    di,xfer_addr    ; ES:DI = requestor's buffer
  371.     mov    si,ax        ; DS:SI = EMSDISK address
  372.     mov    ds,page_frame
  373.     mov    cx,sec_size    ; transfer logical sector from
  374.     cld            ; EMSDISK to requestor
  375.     rep movsb
  376.     push    cs        ; restore local addressing
  377.     pop    ds
  378.     inc    xfer_sec    ; advance sector number
  379.                 ; advance transfer address
  380.     add    word ptr xfer_addr,sec_size
  381.     inc    xfer_cnt    ; count sectors transferred
  382.     jmp    read1
  383.  
  384. read2:                ; all sectors successfully 
  385.     xor    ax,ax        ; transferred, return ok status
  386.  
  387. read3:    les    di,[rh_ptr]    ; get address of Request header
  388.     mov    bx,xfer_cnt    ; and poke in actual transfer count
  389.     mov    es:[di.count],bx ; (in case error aborted transfer
  390.     ret            ;   early)
  391.  
  392. read4:                ; come here if mapping error deteced
  393.     mov    ax,800bh    ; return read fault error code    
  394.     jmp    read3
  395.  
  396. read    endp
  397.  
  398.  
  399. write    proc    near        ; command code 8 = write
  400.                 ; command code 9 = write/verify
  401.  
  402.     call    init_xfer    ; set up local variables 
  403.  
  404. write1: mov    ax,xfer_cnt    ; done with all sectors yet?
  405.     cmp    ax,xfer_req
  406.     je    write2        ; jump if transfer completed
  407.     mov    ax,xfer_sec    ; get next sector number
  408.     call    map_sec     ; and map it
  409.     jc    write4        ; jump if mapping error detected
  410.     mov    di,ax        ; ES:DI = EMSDISK address
  411.     mov    es,page_frame
  412.     lds    si,xfer_addr    ; DS:SI = requestor's buffer
  413.     mov    cx,sec_size    ; transfer logical sector from
  414.     cld            ; requestor to EMSDISK
  415.     rep movsb
  416.     push    cs        ; restore local addressing
  417.     pop    ds
  418.     inc    xfer_sec    ; advance sector number
  419.                 ; advance transfer address
  420.     add    word ptr xfer_addr,sec_size
  421.     inc    xfer_cnt    ; count sectors transferred
  422.     jmp    write1
  423.  
  424. write2:             ; all sectors successfully
  425.     xor    ax,ax        ; transferred, return ok status
  426.  
  427. write3:
  428.     les    di,[rh_ptr]    ; get address of Request header
  429.     mov    bx,xfer_cnt    ; and poke in actual transfer count
  430.     mov    es:[di.count],bx ; (in case error aborted transfer
  431.     ret            ;   early)
  432.  
  433. write4:             ; error detected
  434.     mov    ax,800ah    ; return write fault error code 
  435.     jmp    write3
  436.  
  437. write    endp
  438.  
  439.  
  440. dummy    proc    near        ; this command code routine is
  441.                 ; called for functions that are
  442.                 ; not supported or are applicable
  443.                 ; to character devices only.
  444.  
  445.     xor    ax,ax        ; return success flag for all
  446.     ret
  447.  
  448. dummy    endp
  449.  
  450.     page
  451.  
  452. ;
  453. ; Map into memory a logical "disk" sector from the EMS 
  454. ; pages allocated to the EMSDISK.
  455. ;
  456. ; Called with:    AX    = logical sector number
  457. ;
  458. ; Returns:    CY    = clear if no error
  459. ;        AX    = offset within EMS page frame
  460. ;        AX,CX,DX destroyed
  461. ;
  462. ;        CY    = set if EMM mapping error
  463. ;        AX,CX,DX destroyed
  464. ;
  465. map_sec proc    near    
  466.  
  467.     mov    dx,0        ; divide sector no. by sectors
  468.     mov    cx,sec_per_page ; per page, to get EMS page number
  469.     div    cx        ; now AX=EMS page,DX=rel. sector
  470.     push    dx        ; save remainder
  471.     mov    bx,ax        ; BX <- EMS page number
  472.     mov    ax,4400h    ; map function, phys. page=0
  473.     mov    dx,emm_handle    ; process ID for EMSDISK
  474.     int    67h
  475.  
  476.     or    ah,ah        ; if EMM error, jump to return flag
  477.     jnz    map_sec1
  478.  
  479.     pop    ax        ; get remainder (relative sector)
  480.     mov    cx,sec_size    ; remainder*sec_size to get offset    
  481.     mul    cx        ; into EMS logical page
  482.     clc            ; return CY=clear for no error
  483.     ret            ; back to caller
  484.  
  485. map_sec1:            ; come here if EMM mapping error 
  486.     add    sp,2        ; clear stack
  487.     stc            ; return CY=set for error
  488.     ret
  489.  
  490. map_sec endp
  491.  
  492. ;
  493. ; Set up to perform Read or Write subfunction by copying
  494. ; requestor's buffer address, starting sector, and sector
  495. ; count out of Request header into local variables.
  496. ;
  497. init_xfer proc    near        ; call ES:DI=request header
  498.                 ; extracts addr, start, count
  499.                 ; to working variables
  500.  
  501.     push    es        ; save Request header addr
  502.     push    di
  503.                 ; initialize working variables
  504.                 ; for transfer
  505.     mov    ax,es:[di.sector]
  506.     mov    xfer_sec,ax    ; starting sector number
  507.     mov    ax,es:[di.count]    
  508.     mov    xfer_req,ax    ; sectors requested
  509.     les    di,es:[di.address]
  510.                 ; requestor's buffer offset
  511.     mov    word ptr xfer_addr,di
  512.                 ; requestor's buffer segment
  513.     mov    word ptr xfer_addr+2,es
  514.     mov    xfer_cnt,0    ; init sectors transferred count
  515.     pop    di        ; restore Request header addr
  516.     pop    es
  517.     ret
  518.  
  519. init_xfer endp
  520.  
  521.     page
  522.  
  523. ; The Initialization code for the driver is called only
  524. ; once when the driver is loaded.  For a block device, it
  525. ; must return the number of units, address of an array of
  526. ; pointers to BPBs for each unit, and the address of the
  527. ; first free memory above the driver in the Request Header. 
  528. ; Only MS-DOS services 01-0CH and 30H can be called by the 
  529. ; Initialization function. 
  530. ;
  531. ; "Init" returns its own address to the DOS as the start of 
  532. ; free memory after the driver, so that the memory occupied
  533. ; occupied by "init" and its subroutines will be reclaimed.
  534. ;
  535. init    proc    near        ; command code 0 = Initialize Driver
  536.                 ; on entry ES:DI = request header
  537.  
  538.     les    di,es:[di+18]    ; get addr of CONFIG.SYS line
  539.                 ; and copy it to local buffer
  540.     mov    si,offset cmd_line
  541.     mov    cx,80        ; CX = max line length to copy
  542.  
  543. init0:    mov    al,es:[di]    ; get next character of line
  544.     cmp    al,cr        ; found Carriage Return yet?
  545.     je    init1        ; yes, end of line
  546.     mov    [si],al     ; no, copy this character
  547.     inc    di        ; bump string pointers
  548.     inc    si
  549.     loop    init0        ; loop unless 80 characters copied
  550.  
  551. init1:    mov    ah,30h        ; get DOS version
  552.     int    21h
  553.     mov    dos_ver,al    ; and major version no. for later
  554.     
  555.                 ; set maximum legal command code
  556.     mov    max_cmd,cc2_max ; assume DOS 2.X
  557.     cmp    al,2
  558.     je    init2        ; jump if DOS 2
  559.     mov    max_cmd,cc3_max ; was DOS 3+, set higher value
  560.  
  561. init2:    xor    ax,ax        ; check if EMM driver present
  562.     mov    es,ax        ; if EMM is present, address in
  563.     mov    bx,emm_int*4    ; vector points to EMM driver.
  564.     mov    es,es:[bx+2]    ; now ES:0000 would point to EMM header
  565.     mov    di,10        ; let ES:DI = addr of device name field
  566.                 ; let DS:SI = addr of EMM driver name
  567.     mov    si,offset emm_name
  568.     mov    cx,8        ; length of device name field
  569.     cld
  570.     repz cmpsb        ; compare EMM name to driver header.
  571.     jz    init3        ; jump if strings matched.
  572.     mov    dx,offset msg1    ; if strings didn't match,  
  573.     jmp    init_err    ; driver is absent, exit.
  574.  
  575. init3:    mov    ah,40h        ; EMM driver module is present,
  576.     int    67h        ; test EM Manager status.
  577.     or    ah,ah
  578.     jz    init4        ; jump, driver is OK
  579.     mov    dx,offset msg2    ; EMM is non-functional, print
  580.     jmp    init_err    ; error message and exit 
  581.  
  582. init4:    mov    ah,46h        ; check EM Manager version
  583.     int    67h
  584.     or    ah,ah
  585.     jz    init5        ; jump, got version ok 
  586. init45: mov    dx,offset msg3    ; print EMM error message
  587.     jmp    init_err    ; and exit, discarding driver
  588.  
  589. init5:    cmp    al,030h     ; make sure at least ver. 3.0
  590.     jae    init6        ; jump if EMM version adequate
  591.     mov    dx,offset msg6
  592.     jmp    init_err    ; EMM version too early, exit
  593.  
  594. init6:    mov    ah,41h        ; get page frame segment
  595.     int    67h
  596.     or    ah,ah
  597.     jnz    init45        ; jump if failed to get frame
  598.     mov    page_frame,bx    ; save segment of page frame
  599.  
  600.     mov    ah,42h        ; get number of available pages
  601.     int    67h
  602.     or    ah,ah
  603.     jnz    init45        ; jump if error on get pages
  604.     mov    total_pages,dx    ; save total EMM pages
  605.     mov    avail_pages,bx    ; save available EMM pages
  606.     or    bx,bx        
  607.     jnz    init7        ; proceed if some pages available
  608.     mov    dx,offset msg4
  609.     jmp    init_err    ; no EMS pages left, exit
  610.  
  611. init7:    call    get_kb        ; convert desired size of EMSDISK
  612.  
  613.     mov    ah,43h        ; try and allocate EMM pages
  614.     mov    bx,owned_pages
  615.     int    67h        ; if allocation is successful
  616.     or    ah,ah
  617.     jz    init8        ; jump, allocation was successful
  618.     mov    dx,offset msg5    ; if allocation failed, print
  619.     jmp    init_err    ; error message and exit 
  620.  
  621. init8:    mov    emm_handle,dx    ; save EMM handle for this driver
  622.  
  623.     call    setup        ; set up Bios Parameter Block
  624.  
  625.     call    format        ; format the EMSDISK
  626.     jnc    init9        ; jump if no error during format
  627.     mov    dx,offset msg7    ; formatting error, exit
  628.     jmp    init_err
  629.  
  630. init9:    call    signon        ; format and display driver ident.
  631.  
  632.     les    di,cs:[rh_ptr]    ; restore ES:DI=Request header
  633.  
  634.                 ; return first usable memory addr.
  635.                 ; ("break address") above driver
  636.     mov    word ptr es:[di.address],offset init
  637.     mov    word ptr es:[di.address+2],cs
  638.  
  639.                 ; return EMSDISK logical units = 1
  640.     mov    byte ptr es:[di+13],1
  641.  
  642.                 ; return address of BPB array
  643.     mov    word ptr es:[di+18],offset bpb_array
  644.     mov    word ptr es:[di+20],cs
  645.  
  646.     xor    ax,ax        ; return success status 
  647.     ret
  648.  
  649. init_err:            ; EMM initialization failed,
  650.                 ; print error message and
  651.                 ; discard EMSDISK driver.
  652.  
  653.     push    dx        ; save specific error message
  654.  
  655.     mov    dx,offset ermsg ; print error heading
  656.     mov    ah,9
  657.     int    21h
  658.  
  659.     pop    dx        ; now print error description
  660.     mov    ah,9
  661.     int    21h
  662.  
  663.     les    di,cs:[rh_ptr]    ; restore ES:DI=Request header
  664.  
  665.                 ; discard this driver...
  666.  
  667.                 ; set break addr=start of driver
  668.     mov    word ptr es:[di.address],0
  669.     mov    word ptr es:[di.address+2],cs
  670.  
  671.                 ; set number of logical units=0
  672.     mov    byte ptr es:[di+13],0
  673.  
  674.     xor    ax,ax        ; return status 
  675.     ret
  676.  
  677. init    endp
  678.  
  679.  
  680. setup    proc    near        ; calculate BPB fields depending
  681.                 ; on number of sectors in EMSDISK
  682.     
  683.     mov    ax,owned_pages    ; find total sectors in EMSDISK, 
  684.     mov    cx,sec_per_page ; update BIOS parameter block
  685.     mul    cx
  686.     mov    tot_sec,ax    ; store into (word ptr bpb+8)
  687.  
  688.                 ; determine number of sectors
  689.                 ; per cluster (allocation unit).
  690.                 ; must have tot. clusters <4087
  691.                 ; so can use 12-bit FAT fields.
  692.  
  693.     mov    cx,2        ; start with 2 sectors/cluster
  694.  
  695. setup1: mov    ax,tot_sec    ; try this cluster size...
  696.     mov    dx,0        ; divide total sectors by
  697.     div    cx        ; sectors per cluster.
  698.     cmp    ax,4086     ; resulting clusters < 4087?
  699.     jna    setup2        ; yes, use it
  700.     shl    cx,1        ; sec/cluster*2, try again
  701.     jmp    setup1
  702.  
  703. setup2: mov    sec_clus,cl    ; update sectors per cluster
  704.                 ; in Bios Parameter Block
  705.  
  706.                 ; now AX = total clusters in disk
  707.     mov    dx,ax        ; clusters*1.5 = FAT bytes needed
  708.     add    ax,ax        ; (* 2)
  709.     add    ax,dx        ; (* 3)
  710.     shr    ax,1        ; (/ 2) 
  711.  
  712.     mov    dx,0        ; FAT bytes needed / bytes/sector
  713.     mov    cx,sec_size    ; = number of FAT sectors needed
  714.     div    cx
  715.     or    dx,dx        ; any remainder?
  716.     jz    setup3        ; no,jump
  717.     inc    ax        ; round up to next sector
  718.  
  719. setup3: mov    sec_fat,ax    ; store number of FAT sectors
  720.     ret            ; into (word ptr bpb+0bh)
  721.  
  722. setup    endp
  723.  
  724.  
  725. format    proc    near        ; format the EMSDISK area
  726.                 ; returns CY = clear if successful    
  727.                 ;      CY = set if error
  728.  
  729.     mov    bx,0        ; first clear EMSDISK area
  730.  
  731. fmt1:    cmp    bx,owned_pages    ; done with all EMS pages?
  732.     je    fmt2        ; yes, jump
  733.  
  734.     push    bx        ; save current page number
  735.     mov    ax,4400h    ; map to physical page 0
  736.     mov    dx,emm_handle    ; get our process id
  737.     int    67h        ; request mapping by EMM
  738.  
  739.     pop    bx        ; restore page number
  740.     or    ah,ah        ; if bad mapping give up
  741.     jnz    fmt9        ; (should never happen)
  742.  
  743.     mov    es,page_frame    ; set ES:DI = EMS page
  744.     xor    di,di
  745.     mov    cx,page_size    ; page length 
  746.     xor    al,al        ; fill page with zeros
  747.     cld    
  748.     rep stosb
  749.  
  750.     inc    bx        ; increment page and loop
  751.     jmp    fmt1
  752.  
  753. fmt2:                ; copy phony boot sector with
  754.                 ; Bios Parameter Block to 
  755.                 ; EMSDISK's logical sector 0
  756.  
  757.     mov    ax,0        ; map in logical sector 0
  758.     call    map_sec
  759.     jc    fmt9        ; jump if mapping error
  760.     mov    di,ax        ; let ES:DI = point to sector 0
  761.     mov    es,page_frame
  762.                 ; let DS:SI point to boot rec.
  763.     mov    si,offset boot_rec
  764.     mov    cx,boot_rec_len ; CX = length to copy 
  765.     rep movsb        ; now transfer boot sector
  766.  
  767.     mov    ax,1        ; map in EMSDISK logical 
  768.     call    map_sec     ; sector 1 (first sec of FAT)
  769.     jc    fmt9        ; jump if mapping error
  770.     mov    di,ax
  771.     mov    es,page_frame    ; ES:DI points to sector 1
  772.  
  773.                 ; put media descriptor into FAT
  774.                 ; byte 0, bytes 1-2 must be -1
  775.     mov    al,byte ptr [bpb+0ah]    
  776.     mov    es:[di],al    
  777.     mov    word ptr es:[di+1],-1
  778.  
  779.                 ; now set up EMSDISK volume label.
  780.                 ; first directory sector=
  781.                 ; number of FATs*length of FAT
  782.                 ; plus no. of reserved sectors
  783.     mov    al,byte ptr [bpb+5]
  784.     xor    ah,ah
  785.     mul    word ptr [bpb+0bh]
  786.     add    ax,word ptr [bpb+3]    
  787.     call    map_sec     ; map in first directory block
  788.     jc    fmt9        ; jump if mapping error
  789.     mov    di,ax        ; copy volume label to directory
  790.     mov    es,page_frame
  791.     mov    si,offset vol_name
  792.     mov    cx,vol_name_len
  793.     rep movsb    
  794.  
  795.     clc            ; CY = clear, format successful
  796.     ret
  797.  
  798. fmt9:    stc            ; CY = set if error during format
  799.     ret
  800.  
  801. format    endp
  802.  
  803.  
  804. get_kb    proc    near        ; get desired size of EMSDISK
  805.                 ; in Kbytes from CONFIG.SYS line,
  806.                 ; sets "req_pages" and "owned_pages".
  807.                 ; if no disk size requested by user,
  808.                 ; makes largest possible EMSDISK.
  809.  
  810.     mov    si,offset cmd_line
  811.  
  812. getkb1: lodsb            ; scan for end of driver name
  813.     or    al,al        ; if zero, no Kbytes requested
  814.     jz    getkb9
  815.     cmp    al,blank    ; if blank, reached end of name
  816.     jne    getkb1
  817.  
  818. getkb2: lodsb            ; scan for start of Kbytes field
  819.     or    al,al        ; if zero found, no Kbytes requested
  820.     jz    getkb9    
  821.     cmp    al,blank    ; if blank, keep searching
  822.     je    getkb2
  823.  
  824.     dec    si        ; point to start of field and
  825.     call    ascbin        ; convert string to binary Kbytes
  826.     or    ax,ax        ; if request=0, make big disk
  827.     jz    getkb9
  828.     mov    dx,ax        ; save copy of Kbytes
  829.     mov    cx,4        ; divide Kbytes by 16 to get
  830.     shr    ax,cl        ; requested EMS pages
  831.     and    dx,0fh        ; round up needed?
  832.     jz    getkb3        ; jump if multiple of 16 Kbytes
  833.     inc    ax
  834.  
  835. getkb3: mov    req_pages,ax    ; save requested EMS pages
  836.     cmp    ax,avail_pages    ; compare with pages available
  837.     jna    getkb4        ; jump if ok
  838.     mov    ax,avail_pages    ; request too large, use avail. only
  839.  
  840. getkb4: mov    owned_pages,ax    ; set total EMS pages to allocate
  841.     ret
  842.  
  843. getkb9: mov    ax,avail_pages    ; no size requested by user, set
  844.     mov    req_pages,ax    ; requested and owned to maximum possible.
  845.     mov    owned_pages,ax
  846.     ret
  847.  
  848. get_kb    endp
  849.  
  850.  
  851. signon    proc    near        ; format and print driver ident.
  852.  
  853.     les    di,[rh_ptr]    ; let ES:DI = Request Header.
  854.     mov    al,es:[di+22]    ; get drive code from header,
  855.     add    al,'A'        ; convert it to ASCII, and
  856.     mov    drv_code,al    ; store into sign-on message
  857.  
  858.     mov    ax,cs        ; convert load address to ASCII
  859.     mov    bx,offset dh_addr
  860.     call    hexasc
  861.  
  862.     mov    ax,total_pages    ; format Kbytes of EM installed
  863.     mov    dx,16
  864.     mul    dx        ; pages * 16 = Kbytes
  865.     mov    cx,10
  866.     mov    si,offset kb_installed+3
  867.     call    binasc        ; convert Kbytes to ASCII
  868.  
  869.     mov    ax,avail_pages    ; format Kbytes of EM available
  870.     mov    dx,16
  871.     mul    dx        ; pages * 16 = Kbytes
  872.     mov    cx,10
  873.     mov    si,offset kb_avail+3
  874.     call    binasc        ; convert Kbytes to ASCII
  875.  
  876.     mov    ax,owned_pages    ; format Kbytes assigned to EMSDISK
  877.     mov    dx,16
  878.     mul    dx        ; pages * 16 = Kbytes
  879.     mov    cx,10
  880.     mov    si,offset kb_assigned+3
  881.     call    binasc        ; convert Kbytes to ASCII
  882.  
  883.     mov    ah,9        ; print sign-on message 
  884.     mov    dx,offset ident ; and copyright notice
  885.     int    21h
  886.  
  887.     mov    dx,offset dos2m ; check DOS version, if 
  888.     cmp    dos_ver,2    ; DOS 2 can't know drive letter
  889.     je    signon1
  890.     mov    dx,offset dos3m ; if DOS 3 can display drive
  891. signon1:
  892.     mov    ah,9        ; print load address, bytes
  893.     int    21h        ; in EMSDISK, etc.
  894.  
  895.     ret            ; back to caller
  896.  
  897. signon    endp
  898.  
  899.  
  900. ident        db    cr,lf,lf
  901.         db    'Expanded Memory EMSDISK 1.0'
  902.         db    cr,lf
  903.         db    'Copyright (C) 1986 Ray Duncan'
  904.         db    cr,lf,lf,eom
  905. dos3m        db    'EMSDISK will be drive '
  906. drv_code    db    'X:'
  907.         db    cr,lf
  908. dos2m        db    'Device driver loaded at '
  909. dh_addr     db    'XXXX:0000'
  910.         db    cr,lf,lf
  911. kb_installed    db    '     Kbytes Expanded Memory installed.'
  912.         db    cr,lf
  913. kb_avail    db    '     Kbytes Expanded Memory available.'
  914.         db    cr,lf
  915. kb_assigned    db    '     Kbytes assigned to EMSDISK.'
  916.         db    cr,lf,eom
  917.  
  918. emm_name    db    'EMMXXXX0',0    ; device name for Expanded
  919.                     ; Memory Manager
  920.  
  921. ermsg        db    cr,lf
  922.         db    'EMS RAMDISK installation error:'
  923.         db    cr,lf,eom
  924.  
  925. msg1        db    'Expanded Memory Manager not found.'
  926.         db    cr,lf,eom
  927.  
  928. msg2        db    'Expanded Memory not functional.'
  929.         db    cr,lf,eom
  930.  
  931. msg3        db    'Expanded Memory Manager error.'
  932.         db    cr,lf,eom
  933.  
  934. msg4        db    'No Expanded Memory pages available.'
  935.         db    cr,lf,eom
  936.  
  937. msg5        db    'Expanded Memory allocation failed.'
  938.         db    cr,lf,eom
  939.  
  940. msg6        db    'Wrong Expanded Memory Manager version.'
  941.         db    cr,lf,eom
  942.  
  943. msg7        db    'Unable to format EMSDISK.'
  944.         db    cr,lf,eom
  945.  
  946.                 ; phony volume label, copied to
  947.                 ; EMSDISK's first directory sector
  948. vol_name    db    'EMS_RAMDISK'
  949.         db    08h    ; volume label attribute byte
  950.         db    10 dup (0)
  951.         dw    0    ; time 
  952.         dw    0cb0h    ; date = May 16, 1986
  953.         db    6 dup (0)
  954.  
  955. vol_name_len    equ    $-vol_name
  956.  
  957.  
  958.     page
  959.  
  960. ; HEXASC: convert a binary 16-bit number into
  961. ;      a "hexadecimal" ASCII string.
  962. ;
  963. ; Call with    AX    = value to convert
  964. ;        DS:BX = address to store 4-character string
  965. ;
  966. ; Returns    AX, BX destroyed, other registers preserved
  967. ;
  968. hexasc    proc    near
  969.  
  970.     push    cx        ; save registers 
  971.     push    dx
  972.  
  973.     mov    dx,4        ; initialize character counter
  974.  
  975. hexasc1:
  976.     mov    cx,4        ; isolate next four bits
  977.     rol    ax,cl
  978.     mov    cx,ax
  979.     and    cx,0fh
  980.     add    cx,'0'        ; convert to ASCII 
  981.     cmp    cx,'9'        ; is it 0-9?
  982.     jbe    hexasc2     ; yes, jump
  983.     add    cx,'A'-'9'-1    ; add fudge factor for A-F
  984.  
  985. hexasc2:            ; store this character
  986.     mov    [bx],cl
  987.     inc    bx        ; bump string pointer
  988.  
  989.     dec    dx        ; count characters converted
  990.     jnz    hexasc1     ; loop, not four yet
  991.  
  992.     pop    dx        ; restore registers
  993.     pop    cx
  994.     ret            ; back to caller
  995.  
  996. hexasc    endp
  997.  
  998. ;
  999. ; BINASC: Convert 32 bit binary value to ASCII string.
  1000. ;
  1001. ; Call with  DX:AX = signed 32 bit value
  1002. ;         CX    = radix
  1003. ;         SI    = last byte of area to store resulting string
  1004. ;             (make sure enough room is available to store
  1005. ;              the string in the radix you have selected.)
  1006. ;
  1007. ; Destroys AX, BX, CX, DX, and SI.
  1008. ;
  1009. binasc    proc    near        ; convert DX:AX to ASCII.
  1010.  
  1011.                 ; force storage of at least 1 digit.
  1012.     mov    byte ptr [si],'0' 
  1013.     or    dx,dx        ; test sign of 32 bit value,
  1014.     pushf            ; and save sign on stack.
  1015.     jns    bin1        ; jump if it was positive.
  1016.     not    dx        ; it was negative, take 2's complement
  1017.     not    ax        ; of the value. 
  1018.     add    ax,1
  1019.     adc    dx,0
  1020. bin1:                ; divide the 32 bit value by the radix 
  1021.                 ; to extract the next digit for the
  1022.                 ; forming string.
  1023.     mov    bx,ax        ; is the value zero yet?
  1024.     or    bx,dx
  1025.     jz    bin3        ; yes, we are done converting.
  1026.     call    divide        ; no, divide by radix.
  1027.     add    bl,'0'        ; convert the remainder to an ASCII digit.
  1028.     cmp    bl,'9'        ; we might be converting to hex ASCII,
  1029.     jle    bin2        ; jump if in range 0-9,
  1030.     add    bl,'A'-'9'-1    ; correct it if in range A-F.
  1031. bin2:    mov    [si],bl     ; store this character into string.
  1032.     dec    si        ; back up through string,
  1033.     jmp    bin1        ; and do it again.
  1034. bin3:                ; restore sign flag,
  1035.     popf            ; was original value negative?
  1036.     jns    bin4        ; no, jump
  1037.                 ; yes,store sign into output string.
  1038.     mov    byte ptr [si],'-'
  1039. bin4:    ret            ; back to caller.
  1040.  
  1041. binasc    endp
  1042.  
  1043. ;
  1044. ; General purpose 32 bit by 16 bit unsigned divide.
  1045. ; This must be used instead of the plain machine unsigned divide
  1046. ; for cases where the quotient may overflow 16 bits (for example,
  1047. ; dividing 100,000 by 2).  If called with a zero divisor, this
  1048. ; routine returns the dividend unchanged and gives no warning.
  1049. ;
  1050. ; Call with DX:AX = 32 bit dividend
  1051. ;        CX      = divisor
  1052. ;
  1053. ; Returns   DX:AX = quotient
  1054. ;        BX      = remainder
  1055. ;        CX      = divisor (unchanged)
  1056. ;
  1057. divide    proc    near        ; Divide DX:AX by CX
  1058.  
  1059.     jcxz    div1        ; exit if divide by zero
  1060.     push    ax        ; 0:dividend_upper/divisor
  1061.     mov    ax,dx
  1062.     xor    dx,dx
  1063.     div    cx
  1064.     mov    bx,ax        ; BX = quotient1
  1065.     pop    ax        ; remainder1:dividend_lower/divisor
  1066.     div    cx
  1067.     xchg    bx,dx        ; DX:AX = quotient1:quotient2
  1068.  
  1069. div1:    ret            ; BX = remainder2
  1070.  
  1071. divide    endp
  1072.  
  1073. ;
  1074. ; ASCBIN: Convert decimal ASCII string to unsigned binary integer.
  1075. ;      Conversion ends on first byte not {'0'...'9'}.
  1076. ;
  1077. ; Call with    DS:SI = addr of ASCII string
  1078. ;
  1079. ; Returns    AX    = unsigned binary integer
  1080. ;        DS:SI points to unconvertable character + 1
  1081. ;
  1082. ascbin    proc    near
  1083.  
  1084.     push    dx        ; save prev. register contents
  1085.     xor    dx,dx        ; set forming answer to zero
  1086. ascbin1:
  1087.     lodsb            ; get next character from input string.
  1088.     cmp    al,'9'        ; make sure it is legal character 0-9.
  1089.     ja    ascbin2     ; char > '9', exit with error flag.
  1090.     cmp    al,'0'        
  1091.     jb    ascbin2     ; char < '0', exit with error flag.
  1092.     push    ax        ; save character from string
  1093.     mov    ax,10        ; multiply prev answer * 10
  1094.     mul    dx        ; low part of answer*10 now in AX
  1095.     pop    dx
  1096.     and    dx,0fh        ; isolate binary value 0-9
  1097.     add    dx,ax        ; accumulate forming answer
  1098.     jmp    ascbin1     ; get next character
  1099. ascbin2:            ; end of conversion, 
  1100.     mov    ax,dx        ; return answer in AX
  1101.     pop    dx        ; and restore register DX.
  1102.     ret
  1103.  
  1104. ascbin endp
  1105.  
  1106.  
  1107. code    ends
  1108.     
  1109.     end
  1110. ersion, 
  1111.     mov    ax,dx        ; return answer in AX
  1112.     pop    dx        ; and restore register DX.
  1113.     ret
  1114.  
  1115. asc